#include "esp_log.h"
#include <string.h>
#include "freertos/FreeRTOS.h"


#include "mywifi.h"
#include "mymqtt.h"
#include "my_uart/myuart.h"

extern cleaner* CleanerCar;
extern QueueHandle_t xQueueMqtt2Uart;
extern QueueHandle_t xQueueUart2Mqtt;
static const char *TAG = "MQTT_EXAMPLE";

static char requestIdStr[RESQUESTID_LEN] = {0};

static char respondTopic[] = "$oc/devices/600bb47ab86d7b02dbb3f9ad_84cca8483d0a/sys/commands/response/request_id=d4071ac3-205e-4431-ad71-a061d3d41da5";

static int needUartFlag = 0; //决定是否需要等待串口发来回应

esp_mqtt_client_handle_t client;
    //连接华为云的配置
    esp_mqtt_client_config_t mqtt_cfg = {
        //.uri = "mqtt://mqtt.eclipseprojects.io:1883",
        .host = HOST,
        .port = PORT,
        .client_id = CLIENT_ID,
        .username = USER_NAME,
        .password = PASSWORD,   
    };

//mqtt事件处理函数
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;


    
    switch (event->event_id)
    {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");

        //上传初始状态
        publish_report_property(client, CleanerCar);

        msg_id = esp_mqtt_client_subscribe(client, ISSUED_TOPIC, 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
        break;

    case MQTT_EVENT_SUBSCRIBED:
        //已订阅
        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
        // msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
        // ESP_LOGI(TAG, "sent publish /topic/qos0 successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        //已发布
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        //接收到订阅的消息
        ESP_LOGI(TAG, "MQTT_EVENT_DATA");
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);
        
        //不是很符合原则

        //获得requestid,赋值给全局数组requestIdStr
        getRequestId(event->topic, requestIdStr);
        //先赋值respond的topic
        getRespondTopic(respondTopic, requestIdStr);
        printf("resp topic is \n%s\n", respondTopic);

        mqtt_data_handle(client, (char*) event->data);
        if (needUartFlag == 1)
        {
            //wait uart result 
            static uint8_t receivebuffer[3] = {0};
            xQueueReceive(  xQueueUart2Mqtt, /* 消息队列句柄 */
                            receivebuffer, /* 接收消息队列数据的缓冲地址 */
                            portMAX_DELAY /* 等待消息队列有数据的最大等待时间 */
                            );       
            uartdata_handle(client, receivebuffer);
        }
        //有点粗暴，把全部属性都上传一次，理论上可以单独上传
        publish_report_property(client, CleanerCar);
        break;
    case MQTT_EVENT_ERROR:
        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
        break;
    default:
        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
        break;
    }
    return ESP_OK;
}

static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
    mqtt_event_handler_cb(event_data);
}

void mqtt_start(void)
{
    client = esp_mqtt_client_init(&mqtt_cfg);
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
    esp_mqtt_client_start(client);
}

void mqtt_reconnect(void){
    esp_mqtt_client_start(client);
}

void publish_report_property(esp_mqtt_client_handle_t client, cleaner *cleanerP)
{
    char* str = NULL;
    cJSON* batPtr = cJSON_CreateObject();
    cJSON_AddNumberToObject(batPtr, "charge_state", cleanerP->Battery.ChargeState);
    cJSON_AddNumberToObject(batPtr, "current_val", cleanerP->Battery.CurrentVal);

    cJSON* battServPtr = cJSON_CreateObject();
    cJSON_AddStringToObject(battServPtr, "service_id", "battery");
    cJSON_AddItemToObject(battServPtr, "properties", batPtr);

    cJSON* workPtr = cJSON_CreateObject();
    cJSON_AddNumberToObject(workPtr, "start_time", cleanerP->Work.CarStartTime);
    cJSON_AddNumberToObject(workPtr, "auto_state", cleanerP->Work.WorkState);
    cJSON_AddStringToObject(workPtr, "work_place", cleanerP->Work.WorkPlace);
    
    cJSON* workServPtr = cJSON_CreateObject();
    cJSON_AddStringToObject(workServPtr, "service_id", "work_config");
    cJSON_AddItemToObject(workServPtr, "properties", workPtr);
    
    cJSON* cserv_array = cJSON_CreateArray();
    cJSON_AddItemToArray(cserv_array, workServPtr);
    cJSON_AddItemToArray(cserv_array, battServPtr);

    cJSON* cjsonPtr = cJSON_CreateObject();
    cJSON_AddItemToObject(cjsonPtr, "services", cserv_array);

    str = cJSON_Print(cjsonPtr);
    //printf("%s \n",str);

    esp_mqtt_client_publish(client, REPORT_TOPIC, str, 0, 1, 0);
    cJSON_Delete(cjsonPtr);

    free(str);
    //printf("%s \n",str);
}

void mqtt_data_handle(esp_mqtt_client_handle_t client, char*s){ 
    cJSON *data = cJSON_Parse(s);
    cJSON *jsParas = cJSON_GetObjectItem(data, "paras");
    cJSON *jsCommId = cJSON_GetObjectItem(jsParas, "command_id");
    uint8_t handleResult;
    int commid = jsCommId->valueint;
    
    switch (commid)
    {
    case SETTIME_COMM_ID:
        handleResult = settime_comm_handle(jsParas);
        needUartFlag = 1;     
        break;
    case TIMEQUE_COMM_ID:      
        handleResult = querytime_comm_handle();
        needUartFlag = 1;   
        break;
    case ONOFF_COMM_ID:
        handleResult = onoff_comm_handle(jsParas);
        needUartFlag = 1;   
        break;
    case SET_PLACE_COMM_ID:
        handleResult = setplace_comm_handle(client, jsParas);  
        needUartFlag = 0; 
        break;
    case VALQUE_COMM_ID:
        handleResult = querybatval_comm_handle();
        needUartFlag = 1;          
        break;
    case CHARGE_COMM_ID:
        handleResult = charge_comm_handle();
        needUartFlag = 1;          
        break;
    default:
        break;
    }
}

void uartdata_handle(esp_mqtt_client_handle_t client, uint8_t * receivebuffer){
    uint8_t flag;
    uint8_t battval;
    flag = receivebuffer[0];
    printf("flag is %x \n",flag);
    switch (flag)
    {
    case UART_SET_TIME:
        CleanerCar->Work.CarStartTime = receivebuffer[1]*100 + receivebuffer[2];
        if (CleanerCar->Work.CarStartTime == CleanerCar->Work.TargetStartTime)
        {
            publish_respond_time_command(client, CleanerCar->Work.CarStartTime);
        }else
        {
            ESP_LOGI(TAG, "set time error! target time is %d, time from car is %d\n", CleanerCar->Work.TargetStartTime, CleanerCar->Work.CarStartTime);
        } 
        break;
    case UART_ONOFF:    
        printf("respond onoff : %d \n",receivebuffer[1]);
        publish_respond_onoff_command(client, receivebuffer[1]); 
        break;
    case UART_GET_TIME:
        CleanerCar->Work.CarStartTime = receivebuffer[1]*100 + receivebuffer[2];
        publish_respond_time_command(client, CleanerCar->Work.CarStartTime);
        break;
    case UART_GET_BATVAL:
        CleanerCar->Battery.CurrentVal = receivebuffer[1];
        publish_respond_batval_command(client, CleanerCar->Battery.CurrentVal);

    case UART_ERROR:
        ESP_LOGI(TAG, "GET ERROR MESSAGE");
    break;
    default:
        break;
    } 

}

extern QueueHandle_t xQueueMqtt2Uart;

uint16_t settime_comm_handle(cJSON *jsParas){
    cJSON *jsStartTime = cJSON_GetObjectItem(jsParas, "time");
    starttime startTime = jsStartTime->valueint;
  
    static uint8_t receivebuffer[3] = {0};
    if (startTime<0 || startTime>2400)
    {
        return 1;
    } 

    CleanerCar->Work.TargetStartTime = startTime;
    store_cleaner_time_to_nvs(CleanerCar);

    uint8_t hour = startTime/100;
    uint8_t min  = startTime%100;

    uint8_t array[3] = {UART_SET_TIME,0,0};
    array[1] = hour;
    array[2] = min;

    //send to car,get the start time
    xQueueSend( xQueueMqtt2Uart, /* 消息队列句柄 */
                (const void*)array, /* 要传递数据地址 */
                portMAX_DELAY /* 等待消息队列有空间的最大等待时间 */
              );
    return 0;
}

uint8_t querytime_comm_handle(void){
    //send to car,get the start time
    uint8_t array[3] = {UART_GET_TIME,0,0};
    //send to car,get the start time
    xQueueSend( xQueueMqtt2Uart, /* 消息队列句柄 */
                (const void*)array, /* 要传递数据地址 */
                portMAX_DELAY /* 等待消息队列有空间的最大等待时间 */
              );
    return 0;
}

uint8_t onoff_comm_handle(cJSON *jsParas){
    cJSON *jsOrder = cJSON_GetObjectItem(jsParas, "order");
    int16_t Order = jsOrder->valueint;
    uint8_t array[3] = {UART_ONOFF,0,0};
    
    switch (Order)
    {
    case 10:
        //qidong xiaoche 
        array[1] = 10;
        //send to car,get the start time
        xQueueSend( xQueueMqtt2Uart, /* 消息队列句柄 */
                    (const void*)array, /* 要传递数据地址 */
                    portMAX_DELAY /* 等待消息队列有空间的最大等待时间 */
                );
        break;
    case 20:
        //guanbi xiaoche 
        array[1] = 20;
        //send to car,get the start time
        xQueueSend( xQueueMqtt2Uart, /* 消息队列句柄 */
                    (const void*)array, /* 要传递数据地址 */
                    portMAX_DELAY /* 等待消息队列有空间的最大等待时间 */
                );
        
        break;
    default:
        break;
    }
    return 0;
}

uint8_t setplace_comm_handle(esp_mqtt_client_handle_t client, cJSON *jsParas){
    cJSON *jsWorkplace = cJSON_GetObjectItem(jsParas, "work_place");
    strcpy(CleanerCar->Work.WorkPlace, jsWorkplace->valuestring);
    store_cleaner_place_to_nvs(CleanerCar);
    //发送响应
    publish_respond_setplace_command(client, SETPLACE_RESULT);
    return 0;
}

uint8_t querybatval_comm_handle(void){
    //send to car,get the battery val
    uint8_t array[3] = {UART_GET_BATVAL,0,0};
    xQueueSend( xQueueMqtt2Uart, /* 消息队列句柄 */
                (const void*)array, /* 要传递数据地址 */
                portMAX_DELAY /* 等待消息队列有空间的最大等待时间 */
              );
    return 0;
}

uint8_t charge_comm_handle(void){
    uint8_t array[3] = {UART_CHARGE,0,0};
    xQueueSend( xQueueMqtt2Uart, /* 消息队列句柄 */
                (const void*)array, /* 要传递数据地址 */
                portMAX_DELAY /* 等待消息队列有空间的最大等待时间 */
              );  
    return 0;
}

void publish_respond_batval_command(esp_mqtt_client_handle_t client, uint8_t val){
    char *str = NULL;
    cJSON *paraPtr = cJSON_CreateObject();
    cJSON_AddNumberToObject(paraPtr, "battery_val", val);
    cJSON *respondPtr = cJSON_CreateObject();
    cJSON_AddItemToObject(respondPtr, "paras", paraPtr);

    str = cJSON_Print(respondPtr);
    esp_mqtt_client_publish(client, respondTopic, str, 0, 1, 0);
    cJSON_Delete(respondPtr);
    free(str);
}

void publish_respond_onoff_command(esp_mqtt_client_handle_t client, uint8_t order){
    char *str = NULL;
    cJSON *paraPtr = cJSON_CreateObject();
    cJSON_AddNumberToObject(paraPtr, "state", order);
    cJSON *respondPtr = cJSON_CreateObject();
    cJSON_AddItemToObject(respondPtr, "paras", paraPtr);

    str = cJSON_Print(respondPtr);
    esp_mqtt_client_publish(client, respondTopic, str, 0, 1, 0);
    cJSON_Delete(respondPtr);
    free(str);
}

void publish_respond_setplace_command(esp_mqtt_client_handle_t client, uint8_t result){
    char *str = NULL;
    cJSON *paraPtr = cJSON_CreateObject();
    cJSON_AddNumberToObject(paraPtr, "result", result);
    cJSON *respondPtr = cJSON_CreateObject();
    cJSON_AddItemToObject(respondPtr, "paras", paraPtr);

    str = cJSON_Print(respondPtr);
    esp_mqtt_client_publish(client, respondTopic, str, 0, 1, 0);
    cJSON_Delete(respondPtr);
    free(str);
}

void publish_respond_time_command(esp_mqtt_client_handle_t client, uint16_t time){
    char *str = NULL;
    cJSON *paraPtr = cJSON_CreateObject();
    cJSON_AddNumberToObject(paraPtr, "time", time);
    cJSON *respondPtr = cJSON_CreateObject();
    cJSON_AddItemToObject(respondPtr, "paras", paraPtr);

    str = cJSON_Print(respondPtr);
    esp_mqtt_client_publish(client, respondTopic, str, 0, 1, 0);
    cJSON_Delete(respondPtr);
    free(str);
}


void getRequestId(const char* srcdata, char* desdata){
    int index;
    index = findRequestIdIndex(srcdata, '=');
    srcdata += index;
    //因为华为云的requestid是在最后的，有36位长度，直接复制36字节。
    for (int i = 0; i < RESQUESTID_LEN; i++)
    {
        *desdata = *srcdata;
        desdata++;
        srcdata++;
    }
}


//字符串处理函数
int findRequestIdIndex(char* str, char startChar){
    int srclen = 0;
    srclen = strlen(str);
    for (int i = 0; i < srclen; i++)
    {
        /* code */
        if ( *str == startChar )
        {
            return i+1;
        }else
        {
            str++;
        }
            
    }
    //到结尾还没有找到
    return -1;  
}   


void getRespondTopic(char* respondtopic, const char* reqid){
    int index;
    index = findRequestIdIndex(respondtopic, '=');
    respondtopic += index; 
    for (int i = 0; i < RESQUESTID_LEN; i++)
    {
        *respondtopic = *reqid;
        respondtopic++;
        reqid++;
    }
    
}


